package ArithmeticObjects;
import java.io.IOException;

import utils.ByteTree;

/**
 * Representing a modular group element
 * @author Idan
 */
public class ModularGroupElement extends Element {

	private Group Gq;
	
	/**
	 * creates a ModularGroupElement from the given ByteTree representing it 
	 * and a Group indicating its order
	 * @param bt ByteTree representing the ModularGroupElement
	 * @param Gq Group indicating its order
	 * @throws Exception if the construction failed
	 */
	public ModularGroupElement(ByteTree bt, Group Gq) throws Exception {
		this.Gq = Gq;
		value = bt.getNumber().mod(Gq.getOrder().getValue());
	}
	
	/**
	 * creates a ModularGroupElement from a LargeNumber representing it and a Group indicating its order
	 * @param n LargeNumber representing the ModularGroupElement
	 * @param Gq Group indicating its order
	 * @throws Exception if the construction failed
	 */
	public ModularGroupElement(LargeNumber n, Group Gq) throws Exception{
		value = n;
		this.Gq = Gq;
	}
	
	@Override
	public Element add(Element elementA,
			Element... elements)
			throws Exception {
		if (!(elementA instanceof ModularGroupElement))
			throw new Exception("Wrong class, Expected: ModularGroupElement, got: " 
		+ elementA.getClass().getName());
		LargeNumber val = value.add(elementA.getValue()).mod(Gq.getOrder().getValue());
		for (int i=0; i<elements.length; i++)
		{
			if (!(elements[i] instanceof ModularGroupElement))
				throw new Exception("Wrong class, Expected: ModularGroupElement, got: " 
			+ elements[i].getClass().getName());			
			val = val.add(elements[i].getValue()).mod(Gq.getOrder().getValue());
		}
		return new ModularGroupElement(val, Gq);
	}

	@Override
	public Element subtract(Element elementA)
			throws Exception {
		if (!(elementA instanceof ModularGroupElement))
			throw new Exception("Wrong class, Expected: ModularGroupElement, got: " 
		+ elementA.getClass().getName());
		LargeNumber val = value.subtract(elementA.getValue()).mod(Gq.getOrder().getValue());
		return new ModularGroupElement(val, Gq);
	}

	@Override
	public Element multiply(Element elementA,
			Element... elements)
			throws Exception {
		if (!(elementA instanceof ModularGroupElement))
			throw new Exception("Wrong class, Expected: ModularGroupElement, got: " 
		+ elementA.getClass().getName());
		LargeNumber val = value.multiply(elementA.getValue()).mod(Gq.getOrder().getValue());
		for (int i=0; i<elements.length; i++)
		{
			if (!(elements[i] instanceof ModularGroupElement))
				throw new Exception("Wrong class, Expected: ModularGroupElement, got: " 
			+ elements[i].getClass().getName());
			val = val.multiply(elements[i].getValue()).mod(Gq.getOrder().getValue());
		}
		return new ModularGroupElement(val, Gq);
	}

	@Override
	public Element divide(Element elementA)
			throws Exception {
		if (!(elementA instanceof ModularGroupElement))
			throw new Exception("Wrong class, Expected: ModularGroupElement, got: " 
		+ elementA.getClass().getName());
		LargeNumber val = new LargeNumber(value.multiply(elementA.getValue().
				modInverse(Gq.getOrder().getValue()))).mod(Gq.getOrder().getValue());
		return new ModularGroupElement(val, Gq);
	}

	@Override
	public Element power(Element exponent)
			throws Exception {
		LargeNumber val = value.modPow(exponent.getValue(), Gq.getOrder().getValue());
		return new ModularGroupElement(val, Gq);
	}
	
	@Override
	public Element additiveInverse() throws Exception {
		return new ModularGroupElement(Gq.getOrder().getValue().subtract(this.getValue()),Gq);
	}

	@Override
	public int compareTo(Element elementA) throws Exception {
		if (!(elementA instanceof ModularGroupElement))
			throw new Exception("Wrong class, Expected: ModularGroupElement, got: " 
		+ elementA.getClass().getName());		
		return value.compareTo(elementA.getValue());
	}

	@Override
	public String toString() {
		return value.toString() + "\n";
	}
	
	/**
	 * @return the ModularGroupElement's order
	 */
	public Element getOrder() {
		return Gq.getOrder();
	}
	
	/**
	 * @return the ModularGroupElement's Group
	 */
	public Group getGroup() {
		return Gq;
	}
	
	@Override
	public ByteTree getByteTree() throws IOException {
		return ByteTree.CreateFixedSizeLeaf(value.toByteArray(),(Gq.getOrder().getValue().bitLength()+7)/8);
	}
}
